﻿#region Using

using System;
using System.ServiceModel;

using Common.Base;

using CommunicationLibrary;
using CommunicationLibrary.Converter;
using CommunicationLibrary.TransportData.CommandArgs;

using GameLogic.External;

using ServerApplication.Enums;
using ServerApplication.Interfaces;

#endregion

namespace ServerApplication.Networking
{
    [ServiceBehavior(IncludeExceptionDetailInFaults = false, InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
    public class InConnectionLayer : IServer
    {
        private readonly IClientManager clientManager;

        private readonly INetworkToCore core;

        public InConnectionLayer(INetworkToCore argCore, IClientManager manager)
        {
            this.core = argCore;

            this.clientManager = manager;

            this.InitiateNetworkService();
        }

        public Result CancelRegistration()
        {
            var client = OperationContext.Current.GetCallbackChannel<IClient>();

            lock (client)
            {
                ServerPlayer player;
                if (!this.clientManager.TryFindPlayer(client, out player))
                {
                    // Client ist schon registriert
                    return Result.Failure("Client not registered! Maybe you got disconnected from the server!");
                }

                var coreResult = this.core.CancelRegistration(player);

                if (coreResult != Result.Success)
                {
                    return coreResult;
                }

                player.ServerPlayerState = ServerPlayerState.Disconnected;

                return Result.Success;
            }
        }

        public Result PauseGame()
        {
            var client = OperationContext.Current.GetCallbackChannel<IClient>();

            lock (client)
            {
                ServerPlayer player;
                if (!this.clientManager.TryFindPlayer(client, out player))
                {
                    // Client ist schon registriert
                    return Result.Failure("Client not registered! Maybe you got disconnected from the server!");
                }

                return this.core.PauseGame(player);
            }
        }

        public Result QuitGame()
        {
            var client = OperationContext.Current.GetCallbackChannel<IClient>();

            lock (client)
            {
                ServerPlayer player;
                if (!this.clientManager.TryFindPlayer(client, out player))
                {
                    // Client ist schon registriert
                    return Result.Failure("Client not registered! Maybe you got disconnected from the server!");
                }

                return this.core.QuitGame(player);
            }
        }

        public Result ReadyForGameStart()
        {
            var client = OperationContext.Current.GetCallbackChannel<IClient>();

            lock (client)
            {
                ServerPlayer player;
                if (!this.clientManager.TryFindPlayer(client, out player))
                {
                    // Client ist schon registriert
                    return Result.Failure("Client not registered! Maybe you got disconnected from the server!");
                }

                return this.core.MarkReady(player);
            }
        }

        public Result RegisterForMatch(string name)
        {
            var client = OperationContext.Current.GetCallbackChannel<IClient>();

            OperationContext.Current.Channel.Closed += this.ClientDisconnected;
            OperationContext.Current.Channel.Faulted += this.ClientDisconnected;

            lock (client)
            {
                ServerPlayer player;
                if (!this.clientManager.TryGeneratePlayer(client, name, out player))
                {
                    // Client ist schon registriert
                    return Result.Failure("Client already registered!");
                }

                return this.core.RegisterForMatch(player);
            }
        }

        public Result ResumeGame()
        {
            var client = OperationContext.Current.GetCallbackChannel<IClient>();

            lock (client)
            {
                ServerPlayer player;
                if (!this.clientManager.TryFindPlayer(client, out player))
                {
                    // Client ist schon registriert
                    return Result.Failure("Client not registered! Maybe you got disconnected from the server!");
                }

                return this.core.ResumeGame(player);
            }
        }

        public Result SendBuildCommand(BuildCommandArgs args)
        {
            var client = OperationContext.Current.GetCallbackChannel<IClient>();

            lock (client)
            {
                ServerPlayer player;
                if (!this.clientManager.TryFindPlayer(client, out player))
                {
                    // Client ist schon registriert
                    return Result.Failure("Client not registered! Maybe you got disconnected from the server!");
                }

                var cmd = new BuildCommand(args.Position, BuildingTypeConverter.Convert(args.BuildingType), player);

                return this.core.Execute(cmd);
            }
        }

        public Result SendBuyUpgradeCommand()
        {
            return Result.Failure("Not implemented yet!");
        }

        public Result SendFarmerRouteAddCommand(RouteCommandArgs args)
        {
            var client = OperationContext.Current.GetCallbackChannel<IClient>();

            lock (client)
            {
                ServerPlayer player;
                if (!this.clientManager.TryFindPlayer(client, out player))
                {
                    // Client ist schon registriert
                    return Result.Failure("Client not registered! Maybe you got disconnected from the server!");
                }

                var cmd = new FarmerRouteAddCommand(args.StartPosition, args.EndPosition, player);

                return this.core.Execute(cmd);
            }
        }

        public Result SendFarmerRouteRemoveCommand(RouteCommandArgs args)
        {
            var client = OperationContext.Current.GetCallbackChannel<IClient>();

            lock (client)
            {
                ServerPlayer player;
                if (!this.clientManager.TryFindPlayer(client, out player))
                {
                    // Client ist schon registriert
                    return Result.Failure("Client not registered! Maybe you got disconnected from the server!");
                }

                var cmd = new FarmerRouteRemoveCommand(args.StartPosition, args.EndPosition, player);

                return this.core.Execute(cmd);
            }
        }

        public Result SendMilitiaRouteAddCommand(RouteCommandArgs args)
        {
            var client = OperationContext.Current.GetCallbackChannel<IClient>();

            lock (client)
            {
                ServerPlayer player;
                if (!this.clientManager.TryFindPlayer(client, out player))
                {
                    // Client ist schon registriert
                    return Result.Failure("Client not registered! Maybe you got disconnected from the server!");
                }

                var cmd = new MilitiaRouteAddCommand(args.StartPosition, args.EndPosition, player);

                return this.core.Execute(cmd);
            }
        }

        public Result SendMilitiaRouteRemoveCommand(RouteCommandArgs args)
        {
            var client = OperationContext.Current.GetCallbackChannel<IClient>();

            lock (client)
            {
                ServerPlayer player;
                if (!this.clientManager.TryFindPlayer(client, out player))
                {
                    // Client ist schon registriert
                    return Result.Failure("Client not registered! Maybe you got disconnected from the server!");
                }

                var cmd = new MilitiaRouteRemoveCommand(args.StartPosition, args.EndPosition, player);

                return this.core.Execute(cmd);
            }
        }

        private void InitiateNetworkService()
        {
            var host = new ServiceHost(this);
            host.AddServiceEndpoint(typeof(IServer), FarmersBreakfastTcpBinding.Instance, "net.tcp://localhost:1338/Server");

            host.Open();
        }

        private void ClientDisconnected(object sender, EventArgs e)
        {
            var client = sender as IClient;

            if (client != null)
            {
                lock (client)
                {
                    ServerPlayer player;

                    if (this.clientManager.TryFindPlayer(client, out player))
                    {
                        player.ServerPlayerState = ServerPlayerState.Disconnected;
                    }
                }
            }
        }
    }
}